#! /usr/bin/env python
#coding=utf-8

import os

from .builder import BuilderContext

from .module_builder import ModuleBuilder
from .component_builder import ComponentBuilder

# rank=same https://graphviz.org/Gallery/undirected/grid.html
# 

class GraphVizBuilder(object):
	IGNORE_LIBS = ("libc.so", "libc++.so")

	BUILDER_MAP = {
		"modules": ModuleBuilder(),
		"components": ComponentBuilder()
	}

	def getBuilder(self, name):
		if name in GraphVizBuilder.BUILDER_MAP:
			return GraphVizBuilder.BUILDER_MAP[name]
		return None

	def isModuleIgnored(self, mod):
		return mod["name"] in GraphVizBuilder.IGNORE_LIBS

	def __init__(self, product_mgr, local_file_path="tree", no_cache=False):
		self._product_mgr = product_mgr
		self._no_cache = no_cache

	def _getFilePath(self, product):
		rootPath = self._product_mgr.get_root_path()
		prodPath = os.path.join(rootPath, "graphviz", product["product"], product["version"])
		return prodPath

	def _generateGraphVizFileHeader(self, f):
		f.write("digraph g {\n")

	def _generateGraphVizFileEnd(self, f):
		f.write("}\n")

	def _generateObjectGraph(self, dotFile, name, context):
		with open(dotFile, "w") as f:
			context.setFile(f)
			self._generateGraphVizFileHeader(context.f)
			self.getBuilder(name).generateObjectGraph(context)
			self._generateGraphVizFileEnd(context.f)

	def buildGraph(self, obj, name, xargs):
		context = BuilderContext(self, obj, xargs)

		product = xargs["_cur_product"]
		prodPath = self._getFilePath(product)
		graphPath = os.path.join(prodPath, name, os.path.basename(obj["name"]))

		try:
			os.makedirs(graphPath)
		except:
			pass

		# Generate graphviz dot file first
		fileName = os.path.basename(obj["name"]) + "-" + xargs["type"]
		if "__category" in xargs:
			fileName = fileName + "-" + xargs["__category"]
		dotFile = os.path.join(graphPath, fileName + ".dot")
		if self._no_cache or not os.path.exists(dotFile):
			self._generateObjectGraph(dotFile, name, context)

		# Generate requested file
		fullName = os.path.join(graphPath, fileName + "." + xargs["format"])
		if xargs["format"] != "dot":
			if self._no_cache or not os.path.exists(fullName):
				os.system("dot -T%s '%s' > '%s'" % (xargs["format"], dotFile, fullName))

		with open(fullName, 'rb') as f:
			pngContent = f.read()
		return pngContent

	def _generateCallGraphForMultiModules(self, mgr, xargs, dotFile, modules):
		moduleBulder = self.getBuilder("modules")
		with open(dotFile, "wb") as f:
			context = BuilderContext(self, None, xargs, f)

			self._generateGraphVizFileHeader(f)

			allModules = []
			for mod in modules:
				if not mod:
					continue
				if mod not in allModules:
					allModules.append(mod)
				for m in mod.getAllDependedModules():
					if m not in allModules and not self.isModuleIgnored(m):
						allModules.append(m)

			for mod in allModules:
				context.setObj(mod)
				f.write(moduleBulder.getGraphVizInfo(context))

			for mod in allModules:
				for dep in mod["deps"]:
					if dep["callee"] in allModules:
						context.setObj(dep)
						f.write(moduleBulder.getDepGraphVizInfo(context))

			self._generateGraphVizFileEnd(f)

	def _createProductGraphFile(self, xargs, name):
		product = xargs["_cur_product"]
		prodPath = self._getFilePath(product)
		# Generate graphviz dot file first
		dotFile = os.path.join(prodPath, name + ".dot")
		fullName = os.path.join(prodPath, name + "." + xargs["format"])

		return dotFile, fullName

	def _returnProductGraphFile(self, xargs, dotFile, fullName):
		# Generate requested file
		if xargs["format"] != "dot":
			if self._no_cache or not os.path.exists(fullName):
				os.system("dot -T%s '%s' > '%s'" % (xargs["format"], dotFile, fullName))

		with open(fullName, 'rb') as f:
			pngContent = f.read()
		return pngContent

	def plotChipsetSDKGraph(self, mgr, xargs):
		dotFile, fullName = self._createProductGraphFile(xargs, "chipsetsdk")
		if self._no_cache or not os.path.exists(dotFile):
			sdks = [mod for mod in mgr.get_all() if mod["chipsetsdk"]]
			self._generateCallGraphForMultiModules(mgr, xargs, dotFile, sdks)

		return self._returnProductGraphFile(xargs, dotFile, fullName)

	def plotHdiDepsGraph(self, mgr, xargs):
		dotFile, fullName = self._createProductGraphFile(xargs, "hdi_core")

		if self._no_cache or not os.path.exists(dotFile):
			modules = ("system/lib/chipset-pub-sdk/libhdf_ipc_adapter.z.so", "system/lib/chipset-pub-sdk/libhdf_utils.z.so", "system/lib/chipset-pub-sdk/libhdi.z.so")
			self._generateCallGraphForMultiModules(mgr, xargs, dotFile, [mgr.get_module_by_path(m) for m in modules])

		return self._returnProductGraphFile(xargs, dotFile, fullName)

	def plotByModulesType(self, mgr, xargs):
		mPlotTypes = {
			"chipsetsdk": self.plotChipsetSDKGraph,
			"hdi_core": self.plotHdiDepsGraph
		}
		if xargs and "type" in xargs and xargs["type"] in mPlotTypes:
			return mPlotTypes[xargs["type"]](mgr, xargs)
		return ""

if __name__ == "__main__":
	pass
